package com.oop.example.work.bel;

import antlr.output.MultiFarLexer;
import com.oop.example.processing.Parser;
import com.oop.example.xml.Entry.EntryAttributes;
import com.oop.example.xml.*;
import java.util.HashSet;
import java.util.Properties;
import javax.xml.bind.JAXBElement;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.Token;

public class CommonParser extends Parser {

    private HashSet<EEntryElement> entryElement;

    public CommonParser() {
        super();
    }

    @Override
    public String getDescription() {
        return "";
    }

    @Override
    public boolean isApplicable() {
        MultiFarLexer lexer = new MultiFarLexer(new ANTLRStringStream(this.handler.getBody()));
        Token nextToken = lexer.nextToken();
        while (nextToken.getType() != Token.EOF) {
            if (nextToken.getType() != MultiFarLexer.LABEL
                    && nextToken.getType() != MultiFarLexer.WORD
                    && nextToken.getType() != MultiFarLexer.WS
                    && nextToken.getType() != MultiFarLexer.NO_COMMA
                    && nextToken.getType() != MultiFarLexer.COMMENT
                    && nextToken.getType() != MultiFarLexer.END
                    && nextToken.getType() != MultiFarLexer.COMA
                    && nextToken.getType() != MultiFarLexer.NUMERATION
                    && nextToken.getType() != MultiFarLexer.SEMI
                    && nextToken.getType() != MultiFarLexer.LETTER
                    && nextToken.getType() != MultiFarLexer.BOLD
                    && nextToken.getType() != MultiFarLexer.DOT3
                    && nextToken.getType() != MultiFarLexer.ROMB
                    && nextToken.getType() != MultiFarLexer.COLON
                    && nextToken.getType() != MultiFarLexer.SEE
                    && nextToken.getType() != MultiFarLexer.AMP_BEGIN
                    && nextToken.getType() != MultiFarLexer.AMP_END
                    && nextToken.getType() != MultiFarLexer.NO_DOT) {
                System.out.println("no applicable token " + nextToken.getText() + " ID=" + handler.getEntryPOJO().getID() + " pos=" + lexer.getCharPositionInLine());
                throw new RuntimeException("pos " + lexer.getCharPositionInLine());
                //return false;
            }
            nextToken = lexer.nextToken();
        }
        return true;
    }

    private void insertBody(String body, Near near, Phrase phraseWithAttribs, boolean hasFirstBodyWord) {
        if (body.contains("ROMB")) {
            throw new RuntimeException("Недопустимый токен ROMB в теге simple: " + body);
        }
        Block block = new Block();
        body = body.replaceAll("$", "");
        body = body.replaceAll("@", "");
        body = body.replaceAll("  ", " ");
        body = body.replaceAll("\\^\\|", "(");
        body = body.replaceAll("\\|\\^", ")");

        body = EntryHandler.handleAccentSymbol(block, body);
        body = body.replaceAll("  ", " ");
        block.setValue(body);
        if (hasFirstBodyWord) {
            JAXBElement<Block> simple = factory.createEntryHeadingVocablePartSimple(block);
            near.getNearChildren().add(simple);
        } else {
            JAXBElement<Block> createPhraseSimple = factory.createPhraseSimple(block);
            phraseWithAttribs.getPhraseChildren().add(createPhraseSimple);
        }
    }

    private void insertLabel(String label, Near near, Phrase phraseWithAttribs, boolean hasFirstBodyWord) {
        label = label.replaceAll("<", "").replaceAll(">", "");
        if (Utils.noLabelLabels.contains(label)) {
            entryElement.add(EEntryElement.LABEL_NO_LABEL);
            insertBody(label, near, phraseWithAttribs, hasFirstBodyWord);
            System.out.println("NoLabel >> " + label);
            return;
        }
        Phrase.Label lab = new Phrase.Label();
        lab.setValue(label);
        if (hasFirstBodyWord) {
            JAXBElement<Phrase.Label> partLabel = factory.createEntryHeadingVocablePartLabel(lab);
            near.getNearChildren().add(partLabel);
        } else {
            JAXBElement<Phrase.Label> createPhraseLabel = factory.createPhraseLabel(lab);
            phraseWithAttribs.getPhraseChildren().add(createPhraseLabel);
        }
    }

    private void insertComment(String comment, Near near, Phrase phraseWithAttribs, boolean hasFirstBodyWord) {
        //LabelDictionary ;
        //System.out.println("comment >> " + comment);
        if (comment.contains("[") && comment.contains("]")) {
            handleBoldInComment(comment, phraseWithAttribs);
        } else {

            comment = comment.replaceAll("$", "");
            comment = comment.replaceAll("\\^\\|", "(");
            comment = comment.replaceAll("\\|\\^", ")");
            comment = comment.replaceAll("@", "");
            String contenet = comment.substring(1, comment.length() - 1);
            MultiFarLexer lex = new MultiFarLexer(new ANTLRStringStream(contenet));
            Token token = lex.nextToken();
            Near nearcomment = factory.createNear();
            JAXBElement<Near> partComment = factory.createEntryHeadingVocablePartComment(nearcomment);
            if (hasFirstBodyWord) {
                near.getNearChildren().add(partComment);
            } else {
                phraseWithAttribs.getPhraseChildren().add(partComment);
            }
            // System.out.print(">> :");
            String txt = "(";
            while (token.getType() != Token.EOF) {
                //System.out.print(token.getText());
                if (token.getType() == MultiFarLexer.LABEL) {
                    entryElement.add(EEntryElement.LABEL_IN_COMMENT);
                    if (!txt.isEmpty()) {
                        insertSimpleInComment(txt, nearcomment);
                    }
                    txt = token.getText();
                    txt = txt.replaceAll("<", "").replaceAll(">", "");
                    //if (){
                    if (Utils.noLabelLabels.contains(txt)) {
                        entryElement.add(EEntryElement.LABEL_NO_LABEL);
                        insertSimpleInComment(txt, nearcomment);
                        //System.out.println("NoLabel >> " + txt);
                        txt = "";
                        //}
                    } else {
                        insertLabelInComment(txt, nearcomment);
                        txt = "";
                    }
                } else {
                    txt += token.getText();
                }
                token = lex.nextToken();
            }
            //System.out.println();
            txt += ")";
            if (!txt.isEmpty()) {
                insertSimpleInComment(txt, nearcomment);
            }
        }
    }

    private void insertSimpleInComment(String txt, Near nearcomment) {
        Block block = new Block();
        txt = EntryHandler.handleAccentSymbol(block, txt);
        block.setValue(txt);
        JAXBElement<Block> createPhraseLabel = factory.createPhraseSimple(block);
        nearcomment.getNearChildren().add(createPhraseLabel);
    }

    private void insertLabelInComment(String txt, Near nearcomment) {
        txt = txt.replaceAll("<", "").replaceAll(">", "");
        Phrase.Label lab = new Phrase.Label();
        lab.setValue(txt);
        JAXBElement<Phrase.Label> partLabel = factory.createEntryHeadingVocablePartLabel(lab);
        nearcomment.getNearChildren().add(partLabel);

    }
    Properties ld = Utils.getLabelDictionary();

    private void handleBoldInComment(String comment, Phrase phraseWithAttribs) {
        String txt = EntryHandler.wrapLabels(comment, ld);
        txt = comment.substring(1, comment.length() - 1);
        MultiFarLexer lexer = new MultiFarLexer(new ANTLRStringStream(txt));
        Token nextToken = lexer.nextToken();
        //insertBody("(", null, phraseWithAttribs, false);
        String body = "(";
        while (nextToken.getType() != Token.EOF) {
            switch (nextToken.getType()) {
                case MultiFarLexer.LABEL:
                    if (!body.isEmpty()) {
                        insertBody(body, null, phraseWithAttribs, false);
                    }
                    body = "";
                    String label = nextToken.getText();
                    insertLabel(label, null, phraseWithAttribs, false);
                    nextToken = lexer.nextToken();
                    break;
                case MultiFarLexer.WORD:
                case MultiFarLexer.LETTER:
                case MultiFarLexer.END:
                case MultiFarLexer.NO_COMMA:
                case MultiFarLexer.WS:
                    body += nextToken.getText();
                    nextToken = lexer.nextToken();
                    break;
                case MultiFarLexer.BOLD:
                    if (!body.isEmpty()) {
                        insertBody(body, null, phraseWithAttribs, false);
                    }
                    body = "";
                    phraseWithAttribs.getPhraseChildren().add(factory.createPhraseReference(createRef(nextToken.getText())));
                    nextToken = lexer.nextToken();
                    break;
                default:
                    throw new RuntimeException("pos " + nextToken.getCharPositionInLine());
            }
        }
        body = body + ")";
        insertBody(body, null, phraseWithAttribs, false);
        //System.out.println(EntryHandler.wrapLabels(comment, ld));
    }

    private Reference createRef(String txt) {
        Reference ref = factory.createReference();
        ref.setExternal(factory.createReferenceExternal());
        txt = txt.replace("́", "").replace("[", "").replace("]", "");

        String num = getHomonymNum(txt);
        if (!num.isEmpty()) {
            ref.setReferenceTail(num);
            ref.setViewReferenceTail(num);
        }
        String hdr = getHomonymHeader(txt);
        ref.setDisplayName(hdr);
        ref.setReferencedLexeme(hdr);
        return ref;
    }
    String[] romNum = {"VI", "IV", "V", "III", "II", "I"};
    String[] arabNum = {"6", "4", "5", "3", "2", "1"};

    public String getHomonymHeader(String txt) {
        String homonymHeader = txt;
        for (String s : romNum) {
            if (txt.trim().contains(s)) {
                homonymHeader = homonymHeader.replace(s, "");
            }
        }
        for (String s : arabNum) {
            if (txt.trim().contains(s)) {
                homonymHeader = homonymHeader.replace(s, "");
            }
        }
        return homonymHeader;
    }

    public String getHomonymNum(String txt) {
        String homonymHeader = "";
        for (String s : romNum) {
            if (txt.trim().contains(s)) {
                return s;
            }
        }
        for (int i = 0; i < arabNum.length; i++) {
            String s = arabNum[i];
            if (txt.trim().contains(s)) {
                return "" + romNum[i] + ", " + s + "., " + s + ")"; //homonymHeader = homonymHeader.substring(homonymHeader.indexOf(s));                
            }
        }
        return homonymHeader;
    }

    public static void main(String[] args) {
        String s1 = "(к [часы́)";
        String s2 = "(уменьш. к [круг])";

        //new CommonParser().handleBoldInComment(s1);
        //new CommonParser().handleBoldInComment(s2);

    }

    private Sense createSense(SubSense subSense, int sensNumerator) {
        String[] ssn = {"1)", "2)", "3)", "4)", "5)", "6)", "7)", "8)", "9)", "10)", "11)", "12)", "13)"};
        Sense sense = factory.createSense();
        sense.setNumerator(ssn[sensNumerator]);
        sense.getPronunciationOrClassifierOrTranslation().add(factory.createSenseSubSense(subSense));
        return sense;
    }

    private SubSense createSubSense(Translation translation, int subSensNumerator, JAXBElement<PhraseWithAttribs> subSenseClassifier) {
        String[] ssn = {"a)", "b)", "c)", "d)", "e)", "f)", "g)"};
        SubSense subSense = factory.createSubSense();
        if (subSenseClassifier != null) {
            subSense.getSubSenseChildren().add(subSenseClassifier);
        }
        subSense.getSubSenseChildren().add(factory.createSubSenseTranslation(translation));
        subSense.setNumerator(ssn[subSensNumerator]);
        return subSense;
    }

    @Override
    public void parse() throws ParseError {
        //if (false) {
        entryElement = new HashSet<EEntryElement>();
        if (handler.hasHomonym()) {
            entryElement.add(EEntryElement.HOMONYM);
        }
        MultiFarLexer lexer = new MultiFarLexer(new ANTLRStringStream(this.handler.getBody()));
        Token nextToken = lexer.nextToken();
        int sensNumerator = 0;
        int subSensNumerator = 0;
        PhraseWithAttribs phraseWithAttribs = factory.createPhraseWithAttribs();
        /**
         * Классификатор для SubSense элементов
         */
        JAXBElement<PhraseWithAttribs> subSenseClassifier = null;//factory.createSubSenseClassifier(phraseWithAttribs);

        Near near = factory.createNear();
        Translation.Far far = factory.createTranslationFar();
        Translation translation = factory.createTranslation();

        SubSense subSense = createSubSense(translation, subSensNumerator, subSenseClassifier);//factory.createSubSense();
        Sense sense = createSense(subSense, sensNumerator);

        Variant variant = factory.createVariant();
        variant.setNumerator("1.");
        variant.getPronunciationOrClassifierOrTranslation().add(factory.createVariantSense(sense));

        Homonym homonym = factory.createHomonym();
        homonym.setNumerator("I");
        homonym.getPronunciationOrClassifierOrTranslation().add(factory.createHomonymVariant(variant));

        Entry.Content entryContent = factory.createEntryContent();
        entryContent.getClassifierOrPronunciationOrHomonym().add(factory.createEntryContentHomonym(homonym));
        handler.getEntry().setContent(entryContent);

        EntryAttributes entryAttributes = factory.createEntryEntryAttributes();
        entryAttributes.setIndexInclude(Boolean.TRUE);
        handler.getEntry().setEntryAttributes(entryAttributes);
        /*
         * Признак того что пример следующий далее разрешен
         */
        boolean preBoldSymbol = false;
        boolean hasFirstNumeration = false;
        boolean hasFirstBodyWord = false;
        boolean hasAmpersand = false;
        String body = "";
        while (nextToken.getType() != Token.EOF) {
            switch (nextToken.getType()) {
                case MultiFarLexer.AMP_BEGIN:
                    hasAmpersand = true;
                    //System.out.println("Add AMP classifier");
                    entryElement.add(EEntryElement.AMP);
                    hasFirstBodyWord = false;
                    phraseWithAttribs = factory.createPhraseWithAttribs();
                    subSenseClassifier = factory.createSubSenseClassifier(phraseWithAttribs);
                    subSense.getSubSenseChildren().add(0, subSenseClassifier);

                    nextToken = lexer.nextToken();//мотаем все пробелы
                    //while (nextToken.getType() == MultiFarLexer.WS) {
                    //    nextToken = lexer.nextToken();
                    //}
                    body = "";
                    while (nextToken.getType() != MultiFarLexer.AMP_END) {
                        switch (nextToken.getType()) {
                            case MultiFarLexer.LABEL:
                                if (!body.isEmpty()) {
                                    insertBody(body, near, phraseWithAttribs, hasFirstBodyWord);
                                }
                                body = "";
                                String label = nextToken.getText();
                                insertLabel(label, near, phraseWithAttribs, hasFirstBodyWord);
                                nextToken = lexer.nextToken();
                                break;
                            case MultiFarLexer.COMMENT:
                                if (!body.isEmpty()) {
                                    insertBody(body, near, phraseWithAttribs, hasFirstBodyWord);
                                }
                                body = "";
                                String comment = nextToken.getText();
                                insertComment(comment, near, phraseWithAttribs, hasFirstBodyWord);
                                nextToken = lexer.nextToken();
                                break;
                            case MultiFarLexer.WORD:
                            case MultiFarLexer.LETTER:
                            case MultiFarLexer.COLON:
                                while (nextToken.getType() == MultiFarLexer.COLON
                                        || nextToken.getType() == MultiFarLexer.WORD
                                        || nextToken.getType() == MultiFarLexer.LETTER
                                        || nextToken.getType() == MultiFarLexer.NO_DOT
                                        || nextToken.getType() == MultiFarLexer.NO_COMMA
                                        || nextToken.getType() == MultiFarLexer.WS
                                        || nextToken.getType() == MultiFarLexer.END) {
                                    body += nextToken.getText();
                                    nextToken = lexer.nextToken();
                                }
                                if (nextToken.getType() == MultiFarLexer.AMP_END) {
                                    body = body.substring(0, body.length() - 1);
                                }
                                insertBody(body, near, phraseWithAttribs, hasFirstBodyWord);
                                body = "";
                                break;
                            case MultiFarLexer.COMA:
                            case MultiFarLexer.END:
                            case MultiFarLexer.NO_COMMA:
                            case MultiFarLexer.NO_DOT:
                            case MultiFarLexer.DOT3:
                                while (nextToken.getType() == MultiFarLexer.WS
                                        || nextToken.getType() == MultiFarLexer.END
                                        || nextToken.getType() == MultiFarLexer.NO_DOT
                                        || nextToken.getType() == MultiFarLexer.NO_COMMA
                                        || nextToken.getType() == MultiFarLexer.DOT3
                                        || nextToken.getType() == MultiFarLexer.COMA) {
                                    body += nextToken.getText();
                                    nextToken = lexer.nextToken();
                                }
                                if (nextToken.getType() == MultiFarLexer.AMP_END) {
                                    body = body.substring(0, body.length() - 1);
                                }
                                insertBody(body, near, phraseWithAttribs, hasFirstBodyWord);
                                body = "";
                                break;
                            case MultiFarLexer.WS:
                                body += nextToken.getText();
                                nextToken = lexer.nextToken();
                                break;
                            case MultiFarLexer.BOLD:
                                body += nextToken.getText();

                                phraseWithAttribs.getPhraseChildren().add(factory.createPhraseReference(createRef(body)));
                                body = "";
                                nextToken = lexer.nextToken();
                                break;
                            default:
                                throw new RuntimeException("pos " + nextToken.getCharPositionInLine());
                        }
                    }
                    hasFirstBodyWord = true;
                    break;
                case MultiFarLexer.LABEL://=========================================================
                    entryElement.add(EEntryElement.LABEL);
                    if (!hasFirstBodyWord) {
                        if (subSenseClassifier == null || !subSense.getSubSenseChildren().contains(subSenseClassifier)) {
                            subSenseClassifier = factory.createSubSenseClassifier(phraseWithAttribs);
                            subSense.getSubSenseChildren().add(0, subSenseClassifier);
                        }
                    }
                    if (!body.isEmpty()) {
                        insertBody(body, near, phraseWithAttribs, hasFirstBodyWord);
                    }
                    body = "";
                    String label = nextToken.getText();
                    insertLabel(label, near, phraseWithAttribs, hasFirstBodyWord);
                    nextToken = lexer.nextToken();
                    break;
                case MultiFarLexer.COMMENT://=========================================================
                    entryElement.add(EEntryElement.COMMENT);
                    hasFirstBodyWord = true;
                    if (!hasFirstBodyWord) {
                        if (subSenseClassifier == null || !subSense.getSubSenseChildren().contains(subSenseClassifier)) {
                            subSenseClassifier = factory.createSubSenseClassifier(phraseWithAttribs);
                            subSense.getSubSenseChildren().add(0, subSenseClassifier);
                        }
                    }
                    if (!body.isEmpty()) {
                        insertBody(body, near, phraseWithAttribs, hasFirstBodyWord);
                    }
                    body = "";
                    String comment = nextToken.getText();
                    insertComment(comment, near, phraseWithAttribs, hasFirstBodyWord);
                    nextToken = lexer.nextToken();
                    break;
                case MultiFarLexer.WORD://=========================================================
                case MultiFarLexer.LETTER:
                    entryElement.add(EEntryElement.WORD);
                    //body = "";
                    while (nextToken.getType() == MultiFarLexer.WORD
                            || nextToken.getType() == MultiFarLexer.LETTER
                            || nextToken.getType() == MultiFarLexer.NO_COMMA
                            || nextToken.getType() == MultiFarLexer.NO_DOT
                            || nextToken.getType() == MultiFarLexer.WS
                            || nextToken.getType() == MultiFarLexer.END) {
                        body += nextToken.getText();
                        nextToken = lexer.nextToken();
                    }
                    hasFirstBodyWord = true;
                    insertBody(body, near, phraseWithAttribs, hasFirstBodyWord);
                    body = "";
                    break;
                case MultiFarLexer.END://===========================================================
                case MultiFarLexer.NO_COMMA:
                case MultiFarLexer.NO_DOT:
                case MultiFarLexer.DOT3:
                    entryElement.add((nextToken.getType() == MultiFarLexer.DOT3) ? EEntryElement.DOT3 : EEntryElement.WORD);
                    //body = "";
                    while (nextToken.getType() == MultiFarLexer.WS
                            || nextToken.getType() == MultiFarLexer.END
                            || nextToken.getType() == MultiFarLexer.NO_DOT
                            || nextToken.getType() == MultiFarLexer.NO_COMMA
                            || nextToken.getType() == MultiFarLexer.DOT3) {

                        body += nextToken.getText();
                        nextToken = lexer.nextToken();
                    }

                    insertBody(body, near, phraseWithAttribs, hasFirstBodyWord);
                    body = "";
                    break;
                case MultiFarLexer.WS://===========================================================
                    body += nextToken.getText();
                    nextToken = lexer.nextToken();
                    break;
                case MultiFarLexer.SEMI://===========================================================
                    entryElement.add(EEntryElement.SEMI);
                    translation.getFar().add(far);
                    far.getNear().add(near);
                    far = factory.createTranslationFar();
                    near = factory.createNear();
                    nextToken = lexer.nextToken();
                    preBoldSymbol = true;
                    break;
                case MultiFarLexer.COMA://===========================================================
                    entryElement.add(EEntryElement.COMA);
                    if (!hasFirstBodyWord) {
                        body += ",";
                    } else {
                        far.getNear().add(near);
                        near = factory.createNear();
                    }
                    nextToken = lexer.nextToken();
                    break;
                case MultiFarLexer.BOLD://===========================================================
                    entryElement.add(EEntryElement.EXAMPLE);
                    String txt = nextToken.getText();
                    //txt = txt.replaceAll("\\@", "");
                    txt = txt.replace("[", "");
                    txt = txt.replace("]", "");
                    txt = txt.replaceAll("\\^\\|", "(");
                    txt = txt.replaceAll("\\|\\^", ")");
                    txt = txt.replaceAll("<", "");
                    txt = txt.replaceAll(">", "");
                    if (!preBoldSymbol) {
                        throw new RuntimeException("Тут жирное слово, а впереди нет двоеточия!");
                    }
                    if (txt.matches(".*\\(\\~.*\\).*")) {
                        throw new RuntimeException("Скобки с тильдой в заголовке примера");
                    }
                    //body = "";
                    Example example = factory.createExample();
                    Phrase source = factory.createPhrase();

                    HeaderFormatter headerFormatter = new HeaderFormatter(handler.getHeader(), txt);
                    txt = headerFormatter.getFormatted();
                    if (headerFormatter.hasDotInHeader()) {
                        entryElement.add(EEntryElement.DOTINHEADER);
                    }
                    if (headerFormatter.hasTildaHeader()) {
                        entryElement.add(EEntryElement.TILDA);
                    }
                    insertBody(txt, null, source, false);
                    example.setSource(source);

                    Translation localtranslation = factory.createTranslation();
                    near = factory.createNear();
                    far = factory.createTranslationFar();
                    localtranslation.getFar().add(far);
                    far.getNear().add(near);
                    example.getTranslation().add(localtranslation);
                    subSense.getSubSenseChildren().add(factory.createSubSenseExample(example));
                    nextToken = lexer.nextToken();
                    boolean nextFar = false;
                    //*Необходимо переработать алгоритм парсинга примеров - они могут содержать и дальние и ближние переводы**/
                    while (nextToken.getType() != MultiFarLexer.NUMERATION
                            && nextToken.getType() != MultiFarLexer.ROMB
                            && nextToken.getType() != MultiFarLexer.SEE
                            && nextToken.getType() != MultiFarLexer.COLON
                            && nextToken.getType() != MultiFarLexer.BOLD//SEM
                            && nextToken.getType() != Token.EOF) {
                        if (nextFar) {
                            near = factory.createNear();
                            far = factory.createTranslationFar();
                            localtranslation.getFar().add(far);
                            far.getNear().add(near);
                            entryElement.add(EEntryElement.SEMI_EXAMPLE);
                        }
                        nextFar = false;
                        switch (nextToken.getType()) {
                            case MultiFarLexer.SEMI: //может быть концом дальнего перевода, а может быть концом примера
                                nextToken = lexer.nextToken();//мотаем все пробелы
                                while (nextToken.getType() == MultiFarLexer.WS) {
                                    nextToken = lexer.nextToken();
                                }

                                nextFar = true;//Поднимаем флаг необходимости добавить новый far
                                break;
                            case MultiFarLexer.LABEL:
                                entryElement.add(EEntryElement.LABEL_EXAMPLE);
                                label = nextToken.getText();
                                if (!body.isEmpty()) {
                                    insertBody(body, near, phraseWithAttribs, hasFirstBodyWord);
                                    body = "";
                                }
                                insertLabel(label, near, phraseWithAttribs, hasFirstBodyWord);
                                nextToken = lexer.nextToken();
                                break;
                            case MultiFarLexer.COMMENT:
                                entryElement.add(EEntryElement.COMMENT_EXAMPLE);
                                if (!body.isEmpty()) {
                                    insertBody(body, near, phraseWithAttribs, hasFirstBodyWord);
                                }
                                body = "";
                                comment = nextToken.getText();
                                insertComment(comment, near, phraseWithAttribs, true);
                                nextToken = lexer.nextToken();
                                break;
                            case MultiFarLexer.WORD:
                            case MultiFarLexer.LETTER:
                                entryElement.add(EEntryElement.WORD_EXAMPLE);
                                while (nextToken.getType() == MultiFarLexer.WORD
                                        || nextToken.getType() == MultiFarLexer.LETTER
                                        || nextToken.getType() == MultiFarLexer.NO_COMMA
                                        || nextToken.getType() == MultiFarLexer.NO_DOT
                                        || nextToken.getType() == MultiFarLexer.WS
                                        || nextToken.getType() == MultiFarLexer.END) {
                                    body += nextToken.getText();
                                    nextToken = lexer.nextToken();
                                }
                                hasFirstBodyWord = true;
                                insertBody(body, near, phraseWithAttribs, hasFirstBodyWord);
                                body = "";
                                break;
                            case MultiFarLexer.END:
                            case MultiFarLexer.NO_COMMA:
                            case MultiFarLexer.NO_DOT:
                            case MultiFarLexer.DOT3:
                                entryElement.add((nextToken.getType() == MultiFarLexer.DOT3) ? EEntryElement.DOT3_EXAMPLE : EEntryElement.WORD_EXAMPLE);
                                while (nextToken.getType() == MultiFarLexer.WS
                                        || nextToken.getType() == MultiFarLexer.END
                                        || nextToken.getType() == MultiFarLexer.NO_COMMA
                                        || nextToken.getType() == MultiFarLexer.NO_DOT
                                        || nextToken.getType() == MultiFarLexer.DOT3) {
                                    body += nextToken.getText();
                                    nextToken = lexer.nextToken();
                                }
                                insertBody(body, near, phraseWithAttribs, hasFirstBodyWord);
                                body = "";
                                break;
                            case MultiFarLexer.WS:
                                body += nextToken.getText();
                                nextToken = lexer.nextToken();
                                break;
                            case MultiFarLexer.COMA:
                                entryElement.add(EEntryElement.COMA_EXAMPLE);
                                near = factory.createNear();
                                far.getNear().add(near);
                                nextToken = lexer.nextToken();
                                break;
                            default:
                                System.out.println("bad token " + nextToken.getText());
                                throw new RuntimeException("pos " + nextToken.getCharPositionInLine());
                        }
                    }
                    if (!nextFar) {
                        nextToken = lexer.nextToken();
                        near = factory.createNear();
                        far = factory.createTranslationFar();
                    }
                    //translation.getFar().add(far);
                    break;
                case MultiFarLexer.NUMERATION://=========================================================================
                    entryElement.add(EEntryElement.NUMERATION);
                    hasFirstBodyWord = true;
                    if (!hasAmpersand && subSenseClassifier != null && subSense.getSubSenseChildren().contains(subSenseClassifier)) {
                        //System.out.println("Add variant classifier");
                        variant.getPronunciationOrClassifierOrTranslation().add(0, subSenseClassifier);
                        subSense.getSubSenseChildren().remove(subSenseClassifier);
                    }
                    if (hasFirstNumeration) {
                        sensNumerator++;
                        near = factory.createNear();
                        far = factory.createTranslationFar();
                        if (translation.getFar().isEmpty()) {
                            far.getNear().add(near);
                            translation.getFar().add(far);
                            near = factory.createNear();
                            far = factory.createTranslationFar();
                        }
                        translation = factory.createTranslation();
                        //translation.getFar().add(far);
                        subSense = createSubSense(translation, subSensNumerator, null);//factory.createSubSense();
                        sense = createSense(subSense, sensNumerator);
                        variant.getPronunciationOrClassifierOrTranslation().add(factory.createVariantSense(sense));
                    }
                    String numInTxt = nextToken.getText();
                    String numInParser = (sensNumerator + 1) + ".";
                    if (!numInTxt.equalsIgnoreCase(numInParser)) {
                        throw new RuntimeException("Нумерация в исходнике не соответствует ожидаемой: " + numInTxt + " != " + numInParser);
                    }
                    nextToken = lexer.nextToken();
                    while (nextToken.getType() == MultiFarLexer.WS) {
                        body = "";
                        //System.out.println("Перемотка");
                        nextToken = lexer.nextToken();
                    }
                    hasFirstNumeration = true;
                    break;
                case MultiFarLexer.SEE://===========================================================
                    entryElement.add(EEntryElement.SEE);
                    label = nextToken.getText();
                    label = label.replace("{", "").replace("}", "");
                    insertLabel(label, near, phraseWithAttribs, true);
                    nextToken = lexer.nextToken();
                    txt = "";
                    while (nextToken.getType() != MultiFarLexer.SEMI
                            && nextToken.getType() != Token.EOF) {
                        txt += nextToken.getText();
                        nextToken = lexer.nextToken();
                    }
                    txt = txt.trim().replace("[", "").replace("]", "").replace("\u0301", "");
                    txt = txt.replace("COMA", ",");
                    //System.out.println("ref: " + txt);
                    Reference ref = factory.createReference();
                    ref.setDisplayName(txt);
                    ref.setReferencedLexeme(txt);
                    ref.setExternal(factory.createReferenceExternal());
                    near.getNearChildren().add(factory.createNearReference(ref));
                    break;
                case MultiFarLexer.COLON://===========================================================
                    entryElement.add(EEntryElement.COLON);
                    nextToken = lexer.nextToken();
                    while (nextToken.getType() == MultiFarLexer.WS) {//пропускаем все пробелы
                        nextToken = lexer.nextToken();
                    }
                    near = factory.createNear();
                    far = factory.createTranslationFar();
                    translation.getFar().add(far);
                    far.getNear().add(near);
                    while (nextToken.getType() != Token.EOF) {
                        txt = "";
                        do {
                            txt += nextToken.getText();
                            nextToken = lexer.nextToken();
                        } while (nextToken.getType() != MultiFarLexer.BOLD
                                && nextToken.getType() != Token.EOF);
                        txt = txt.replaceAll("\\@", "");
                        EntryHandler subHandler = new EntryHandler(txt, handler.getHeader());
                        try {

                            subHandler.liteHandle();

                            handler.getChildEntrys().add(subHandler.getEntry());
                            PhraseWithAttribs combination = factory.createPhraseWithAttribs();
                            ref = factory.createReference();
                            String header = subHandler.getHeader();
                            header = header.trim().replace("\u0301", "");
                            header = header.replace("COMA", ",");
                            ref.setDisplayName(header);
                            ref.setReferencedLexeme(header);
                            ref.setExternal(factory.createReferenceExternal());
                            combination.getPhraseChildren().add(factory.createPhraseReference(ref));
                            subSense.getSubSenseChildren().add(factory.createSenseCombination(combination));

                        } catch (Exception ex) {
                            ex.printStackTrace(System.out);
                            throw new ParseError();
                        }
                        //nextToken = lexer.nextToken();
                    }
                    break;
                case MultiFarLexer.ROMB:
                    entryElement.add(EEntryElement.ROMB);
                    nextToken = lexer.nextToken();
                    while (nextToken.getType() == MultiFarLexer.WS) {//пропускаем все пробелы
                        nextToken = lexer.nextToken();
                    }
                    hasFirstBodyWord = false;
                    translation.getFar().remove(far);
                    while (nextToken.getType() != Token.EOF
                            && nextToken.getType() != MultiFarLexer.NUMERATION) {
                        txt = "";
                        do {
                            txt += nextToken.getText();
                            nextToken = lexer.nextToken();
                        } while (nextToken.getType() != MultiFarLexer.BOLD
                                && nextToken.getType() != Token.EOF
                                && nextToken.getType() != MultiFarLexer.NUMERATION);
                        //txt = txt.replaceAll("\\@", "");
                        txt = txt.replaceAll("\\^\\|", "(");
                        txt = txt.replaceAll("\\|\\^", ")");
                        EntryHandler subHandler = new EntryHandler(txt, handler.getHeader());
                        try {
                            //try {
                            subHandler.liteHandle();
                            //} catch (Exception ex) {
                            //    ex.printStackTrace(System.out);
                            //}
                            handler.getChildEntrys().add(subHandler.getEntry());
                            String[] headers = subHandler.getHeaders();
                            for (String header : headers) {
                                PhraseWithAttribs combination = factory.createPhraseWithAttribs();
                                ref = factory.createReference();
                                header = header.trim().replace("\u0301", "");
                                header = header.replace("COMA", ",");
                                ref.setDisplayName(header);
                                ref.setReferencedLexeme(header);
                                ref.setExternal(factory.createReferenceExternal());
                                combination.getPhraseChildren().add(factory.createPhraseReference(ref));
                                subSense.getSubSenseChildren().add(factory.createSenseCombination(combination));
                            }
                        } catch (Exception ex) {
                            ex.printStackTrace(System.out);
                            throw new RuntimeException(ex.getMessage());
                        }
                        //nextToken = lexer.nextToken();
                    }
                    break;
                default://=========================================================================
                    nextToken = lexer.nextToken();
                    break;
            }
        }

        if (translation.getFar().isEmpty()) {
            near = factory.createNear();
            far = factory.createTranslationFar();
            far.getNear().add(near);
            translation.getFar().add(far);
        }
    }

    @Override
    public String getEntryType() {
        String mask = "";
        for (EEntryElement ee : EEntryElement.values()) {
            mask += ee.toString() + ":" + entryElement.contains(ee) + "#";
        }
        mask = mask.substring(0, mask.length() - 1);
        return mask;
    }
}
